<!DOCTYPE html>
<!--
Copyright 2016 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/tracing/core/test_utils.html">
<link rel="import" href="/tracing/extras/chrome/blame_context/frame_tree_node.html">
<link rel="import" href="/tracing/extras/chrome/blame_context/render_frame.html">
<link rel="import" href="/tracing/extras/chrome/blame_context/top_level.html">
<link rel="import" href="/tracing/ui/extras/side_panel/frame_data_side_panel.html">

<script>
'use strict';

tr.b.unittest.testSuite(function() {
  const TestUtils = tr.c.TestUtils;

  function topLevelOptions(pid, id) {
    return {
      pid,
      id,
      cat: 'blink',
      scope: 'PlatformThread',
      name: 'TopLevel'
    };
  }

  function renderFrameOptions(pid, id, parent) {
    return {
      pid,
      id,
      cat: 'blink',
      scope: 'RenderFrame',
      name: 'RenderFrame',
      args: {parent: {
        id_ref: parent.id,
        scope: parent.scope
      }}
    };
  }

  function frameTreeNodeOptions(pid, id, opt_renderFrame, opt_parentId) {
    const ans = {
      pid,
      id,
      cat: 'navigation',
      scope: 'FrameTreeNode',
      name: 'FrameTreeNode',
      args: {}
    };
    if (opt_renderFrame) {
      ans.args.renderFrame = {
        id_ref: opt_renderFrame.id,
        pid_ref: opt_renderFrame.pid,
        scope: 'RenderFrame'
      };
    }
    if (opt_parentId) {
      ans.args.parent = {
        id_ref: opt_parentId,
        scope: 'FrameTreeNode'
      };
    }
    return ans;
  }

  /**
   * Creates some independent contexts. Checks if all are present in the panel.
   */
  test('basic', function() {
    const panel = document.createElement('tr-ui-e-s-frame-data-side-panel');
    panel.model = TestUtils.newModel(function(model) {
      TestUtils.newSnapshot(model, topLevelOptions(1, '0x1'));
      TestUtils.newSnapshot(model, renderFrameOptions(
          1, '0x2', {id: '0x1', scope: 'PlatformThread'}));
      TestUtils.newSnapshot(model, frameTreeNodeOptions(
          2, '0x3'));
    });
    assert.lengthOf(panel.$.table.tableRows, 3);

    this.addHTMLOutput(panel);
  });

  /**
   * Creates a FrameTreeNode in the browser process and a RenderFrame in a
   * renderer process that are the same frame. Checks if they are merged into
   * one row in the panel.
   */
  test('mergeCrossProcessFrameBlameContexts', function() {
    const panel = document.createElement('tr-ui-e-s-frame-data-side-panel');
    panel.model = TestUtils.newModel(function(model) {
      TestUtils.newSnapshot(model, topLevelOptions(1, '0x1'));
      TestUtils.newSnapshot(model, renderFrameOptions(
          1, '0x2', {id: '0x1', scope: 'PlatformThread'}));
      TestUtils.newSnapshot(model, frameTreeNodeOptions(
          2, '0x3', {id: '0x2', pid: 1}));
    });
    assert.lengthOf(panel.$.table.tableRows, 2);

    this.addHTMLOutput(panel);
  });

  function newAttributedSlice(model, pid, start, duration, context) {
    const slice = TestUtils.newSliceEx({start, duration});
    slice.contexts = [{type: 'FrameBlameContext', snapshot: context}];
    model.getOrCreateProcess(pid).getOrCreateThread(1).sliceGroup.pushSlice(
        slice);
    return slice;
  }

  /**
   * Changes the range of interest. Checks if the panel updates correspondingly.
   */
  test('respondToRangeOfInterest', function() {
    let topLevel;
    let slice1;
    let slice2;
    const panel = document.createElement('tr-ui-e-s-frame-data-side-panel');
    panel.model = TestUtils.newModel(function(model) {
      topLevel = TestUtils.newSnapshot(model, topLevelOptions(1, '0x1'));
      slice1 = newAttributedSlice(model, 1, 1500, 500, topLevel);
      slice2 = newAttributedSlice(model, 1, 2500, 500, topLevel);
    });

    // The default range of interest contains both slices.
    assert.isTrue(panel.$.table.tableRows[0].eventsOfInterest.equals(
        new tr.model.EventSet([topLevel, slice1, slice2])));

    // The new range of interest contains only slice2.
    panel.rangeOfInterest = tr.b.math.Range.fromExplicitRange(slice2.start,
        slice2.end);
    assert.isTrue(panel.$.table.tableRows[0].eventsOfInterest.equals(
        new tr.model.EventSet([topLevel, slice2])));

    this.addHTMLOutput(panel);
  });

  /**
   * Selects a row in the panel. Checks if the context(s) of the row and the
   * slices attributed to the row are selected.
   */
  test('selectAttributedEvents', function() {
    let topLevel;
    let slice;
    const panel = document.createElement('tr-ui-e-s-frame-data-side-panel');
    panel.model = TestUtils.newModel(function(model) {
      topLevel = TestUtils.newSnapshot(model, topLevelOptions(1, '0x1'));
      slice = newAttributedSlice(model, 1, 1500, 500, topLevel);
    });

    let selectionChanged = false;
    panel.addEventListener('requestSelectionChange', function(e) {
      selectionChanged = true;
      assert.isTrue(
          e.selection.equals(new tr.model.EventSet([topLevel, slice])));
    });
    panel.$.table.selectedTableRow = panel.$.table.tableRows[0];
    assert.isTrue(selectionChanged);

    this.addHTMLOutput(panel);
  });
});
</script>
